package com.afteryuan.dao;


import com.afteryuan.util.PaginationBean;
import com.kmpro.cicada.wing.analysis.ComplexAnalyzer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.queryParser.MultiFieldQueryParser;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.Criterion;
import org.hibernate.search.FullTextSession;
import org.hibernate.search.Search;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataRetrievalFailureException;
import org.springframework.util.Assert;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 负责为单个Entity对象提供CRUD操作的Hibernate DAO基类.
 * <p/>
 * 子类只要在类定义时指定所管理Entity的Class, 即拥有对单个Entity对象的CRUD操作.
 * <p/>
 * <p><a href="EntityDao.java.html"><i>View Source</i></a></p>
 *
 * @author <a href="mailto:afteryuan@afteryuan.com">$Author: yangc $</a>
 * @version $Revision: 1.2 $
 * @see HibernateGenericDao
 */
@SuppressWarnings("unchecked")
public class HibernateEntityDao<T> extends HibernateGenericDao implements EntityDao<T> {
    private static Log log = LogFactory.getLog(HibernateEntityDao.class);
    protected Class<T> entityClass;// DAO所管理的Entity类型.

    /**
     * 在构造函数中将泛型T.class赋给entityClass.
     */
    public HibernateEntityDao() {
        entityClass = GenericsUtils.getSuperClassGenricType(getClass());
        //log.debug(entityClass);

    }

    /**
     * 取得entityClass.JDK1.4不支持泛型的子类可以抛开Class<T> entityClass,重载此函数达到相同效果。
     */
    protected Class<T> getEntityClass() {
        return entityClass;
    }

    /**
     * 根据ID获取对象.
     *
     * @see HibernateGenericDao#getId(Class,Object)
     */
    public T get(Serializable id) {
        return get(getEntityClass(), id);
    }

    public T load(Serializable id) {
        return load(getEntityClass(), id);
    }

    /**
     * 获取全部对象
     *
     * @see HibernateGenericDao#getAll(Class)
     */
    public List<T> getAll() {
        return getAll(getEntityClass());
    }

    /**
     * 获取全部对象,带排序参数.
     *
     * @see HibernateGenericDao#getAll(Class,String,boolean)
     */
    public List<T> getAll(String orderBy, boolean isAsc) {
        return getAll(getEntityClass(), orderBy, isAsc);
    }

    /**
     * 根据ID移除对象.
     *
     * @see HibernateGenericDao#removeById(Class,Serializable)
     */
    public void removeById(Serializable id) {
        removeById(getEntityClass(), id);
    }

    /**
     * 取得Entity的Criteria.
     *
     * @see HibernateGenericDao#createCriteria(Class,Criterion[])
     */
    public Criteria createCriteria(Criterion... criterions) {
        return createCriteria(getEntityClass(), criterions);
    }

    /**
     * 取得Entity的Criteria,带排序参数.
     *
     * @see HibernateGenericDao#createCriteria(Class,String,boolean,Criterion[])
     */
    public Criteria createCriteria(String orderBy, boolean isAsc, Criterion... criterions) {
        return createCriteria(getEntityClass(), orderBy, isAsc, criterions);
    }

    /**
     * 根据属性名和属性值查询对象.
     *
     * @return 符合条件的对象列表
     * @see HibernateGenericDao#findBy(Class,String,Object)
     */
    public List<T> findBy(String propertyName, Object value) {
        return findBy(getEntityClass(), propertyName, value);
    }

    /**
     * 根据属性名和属性值查询对象,带排序参数.
     *
     * @return 符合条件的对象列表
     * @see HibernateGenericDao#findBy(Class,String,Object,String,boolean)
     */
    public List<T> findBy(String propertyName, Object value, String orderBy, boolean isAsc) {
        return findBy(getEntityClass(), propertyName, value, orderBy, isAsc);
    }

    /**
     * 根据属性名和属性值查询单个对象.
     *
     * @return 符合条件的唯一对象 or null
     * @see HibernateGenericDao#findUniqueBy(Class,String,Object)
     */
    public T findUniqueBy(String propertyName, Object value) {
        return findUniqueBy(getEntityClass(), propertyName, value);
    }

    /**
     * 判断对象某些属性的值在数据库中唯一.
     *
     * @param uniquePropertyNames 在POJO里不能重复的属性列表,以逗号分割 如"name,loginid,password"
     * @see HibernateGenericDao#isUnique(Class,Object,String)
     */
    public boolean isUnique(Object entity, String uniquePropertyNames) {
        return isUnique(getEntityClass(), entity, uniquePropertyNames);
    }

    /**
     * 创建查询列表,默认返回类型为 Service Entity 类型
     *
     * @param hql
     * @param values
     * @return null if no result
     */
    public List<T> createList(String hql, Object... values) {
        PaginationBean paginationBean = null;
        if (values != null && values.length > 0 && values[0] instanceof PaginationBean) {
            paginationBean = (PaginationBean) values[0];
            Object[] values2 = new Object[values.length - 1];
            System.arraycopy(values, 1, values2, 0, values.length - 1);
            values = values2;
        }

        Query query = createQuery(hql, values);
        if (paginationBean != null) {
            if (paginationBean.getTotalPages() == 0) paginationBean.setTotalResult((int) countQuery(hql, values));
            query.setMaxResults(paginationBean.getLength()).setFirstResult(paginationBean.getStart());
        } else {
            if (countQuery(hql, values) > 5000) {
//                log.debug("hql:"+hql);
//                log.warn("Result of List  more than 5000 will discard");
            }
        }
        List list = query.list();
        if (list.size() == 0) return null;
        return (List<T>) list;
    }

    /**
     * 创建查询列表,默认返回类型为传入的类型
     *
     * @param entityName
     * @param hql
     * @param values
     * @return List<K>
     */

    public <K> List<K> createList(Class<K> entityName, String hql, Object... values) {
        PaginationBean paginationBean = null;
        if (values != null && values.length > 0 && values[0] instanceof PaginationBean) {
            paginationBean = (PaginationBean) values[0];
            Object[] values2 = new Object[values.length - 1];
            System.arraycopy(values, 1, values2, 0, values.length - 1);
            values = values2;
        }
        Query query = createQuery(hql, values);
        if (paginationBean != null) {
            if (paginationBean.getTotalPages() == 0) paginationBean.setTotalResult(countQuery(hql, values));
            query.setMaxResults(paginationBean.getLength()).setFirstResult(paginationBean.getStart());
        }
        List list = query.list();
        if (list.size() == 0) return null;
        return (List<K>) list;
    }

    /**
     * 使用 hibernate search 进行全文检索
     *
     * @param lql    查询字符串
     * @param field  默认字段，如果查询串中没有指定，者使用此字段
     * @param values 可变参数，如果要分页，则传入分页bean
     * @return List<K>
     * @throws DataAccessException 数据库访问异常
     */
    public <K> List<K> fullTextSearch(String lql, String field, Object... values) throws DataAccessException {
        PaginationBean paginationBean = null;
        if (values != null && values.length > 0 && values[0] instanceof PaginationBean)
            paginationBean = (PaginationBean) values[0];
        Session session = getSession();
        FullTextSession fullTextSession = Search.getFullTextSession(session);
        org.apache.lucene.queryParser.QueryParser parser = new QueryParser(field, new ComplexAnalyzer());
        org.apache.lucene.search.Query luceneQuery = null;
        try {
            luceneQuery = parser.parse(lql);
        } catch (ParseException e) {
            throw new DataRetrievalFailureException("lucene parser failure");
        }
        org.hibernate.search.FullTextQuery fullTextQuery = fullTextSession.createFullTextQuery(luceneQuery, entityClass);
        if (paginationBean != null) {
            paginationBean.setTotalResult(fullTextQuery.getResultSize());
            fullTextQuery.setMaxResults(paginationBean.getLength()).setFirstResult(paginationBean.getStart());
        }
        return (List<K>) fullTextQuery.list();

    }



    /**
     * 多字段全文搜索
     *
     * @param lql
     * @param fields
     * @param values
     * @param <K>
     * @return
     */
    public <K> List<K> mutiFieldFullTextSearch(String lql, List<String> fields, Object... values) {
        PaginationBean paginationBean = null;
        if (values != null && values.length > 0 && values[0] instanceof PaginationBean)
            paginationBean = (PaginationBean) values[0];
        Session session = getSession();
        FullTextSession fullTextSession = Search.getFullTextSession(session);
        org.apache.lucene.search.Query luceneQuery = null;
        try {
            luceneQuery = new MultiFieldQueryParser(fields.toArray(new String[fields.size()]), new ComplexAnalyzer()).parse(lql); //在title和content中找word
        } catch (ParseException e) {
            throw new DataRetrievalFailureException("lucene parser failure");
        }
        org.hibernate.search.FullTextQuery fullTextQuery = fullTextSession.createFullTextQuery(luceneQuery, entityClass);
        if (paginationBean != null) {
            paginationBean.setTotalResult(fullTextQuery.getResultSize());
            fullTextQuery.setMaxResults(paginationBean.getLength()).setFirstResult(paginationBean.getStart());
        }
        List list = fullTextQuery.list();
        if (list.size() == 0) return null;
        return (List<K>) list;

    }


    /**
     * 计算数量
     *
     * @param hql    hql
     * @param values 查询条件值
     * @return count
     */
    public long countQuery(String hql, Object... values) {
        Assert.hasText(hql);
        // Count查询
        String countQueryString = " select count (*) " + removeSelect(removeOrders(hql));
        List countlist = getHibernateTemplate().find(countQueryString, values);
        return (Long) countlist.get(0);
    }

    /**
     * 去除hql的select 子句，未考虑union的情况,用于pagedQuery.
     */
    private static String removeSelect(String hql) {
        Assert.hasText(hql);
        int beginPos = hql.toLowerCase().indexOf("from");
        Assert.isTrue(beginPos != -1, " hql : " + hql + " must has a keyword 'from'");
        return hql.substring(beginPos);
    }

    /**
     * 去除hql的orderby 子句，用于pagedQuery.
     */
    private static String removeOrders(String hql) {
        Assert.hasText(hql);
        Pattern p = Pattern.compile("order\\s*by[\\w|\\W|\\s|\\S]*", Pattern.CASE_INSENSITIVE);
        Matcher m = p.matcher(hql);
        StringBuffer sb = new StringBuffer();
        while (m.find()) {
            m.appendReplacement(sb, "");
        }
        m.appendTail(sb);
        return sb.toString();
    }
}

class GenericsUtils {
    private static final Log log = LogFactory.getLog(GenericsUtils.class);

    private GenericsUtils() {
    }

    /**
     * 通过反射,获得定义Class时声明的父类的范型参数的类型. 如public BookManager extends GenricManager<Book>
     *
     * @param clazz The class to introspect
     * @return the first generic declaration, or <code>Object.class</code> if cannot be determined
     */
    public static Class getSuperClassGenricType(Class clazz) {
        return getSuperClassGenricType(clazz, 0);
    }

    /**
     * 通过反射,获得定义Class时声明的父类的范型参数的类型. 如public BookManager extends GenricManager<Book>
     *
     * @param clazz clazz The class to introspect
     * @param index the Index of the generic ddeclaration,start from 0.
     * @return the index generic declaration, or <code>Object.class</code> if cannot be determined
     */
    public static Class getSuperClassGenricType(Class clazz, int index) {

        Type genType = clazz.getGenericSuperclass();

        if (!(genType instanceof ParameterizedType)) {
            //log.warn(clazz.getSimpleName() + "'s superclass not ParameterizedType");
            return Object.class;
        }


        Type[] params = ((ParameterizedType) genType).getActualTypeArguments();

        if (index >= params.length || index < 0) {
            log.warn("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: "
                    + params.length);
            return Object.class;
        }
        if (!(params[index] instanceof Class)) {
            log.warn(clazz.getSimpleName() + " not set the actual class on superclass generic parameter");
            return Object.class;
        }
        return (Class) params[index];
    }

}

